package threads;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author John Dickerson
 */
public class AtomicReferenceTimeUpdater
{

    public static void main(String[] args) throws InterruptedException, ExecutionException
    {
        Time cow = new Time(0, 0);
        CountDownLatch latch = new CountDownLatch(1);

        ExecutorService executor = Executors.newCachedThreadPool();

        MyCall t1 = new MyCall(cow, latch);
        MyCall t2 = new MyCall(cow, latch);
        MyCall t3 = new MyCall(cow, latch);
        Master master = new Master(cow, latch);

        Thread masterThread = new Thread(master);
        masterThread.setDaemon(true);

        Future<String> r1 = executor.submit(t1);
        Future<String> r2 = executor.submit(t2);
        Future<String> r3 = executor.submit(t3);
        executor.submit(masterThread);

        executor.shutdown();
        latch.countDown();

        System.out.println(r1.get());
        System.out.println(r2.get());
        System.out.println(r3.get());

        System.out.println(cow);
    }
}

class MyCall implements Callable<String>
{

    private Time cow;
    private CountDownLatch latch;

    public MyCall(Time cow, CountDownLatch latch)
    {
        this.cow = cow;
        this.latch = latch;
    }

    public String call() throws Exception
    {
        try
        {
            latch.await();
        } catch (InterruptedException ex)
        {
            Logger.getLogger(MyCall.class.getName()).log(Level.SEVERE, null, ex);
        }

        for (int i = 0; i < 1000000; i++)
        {
            cow.incrementTime();
        }

        return Thread.currentThread().getName() + " finished.";
    }
}

class Master implements Runnable
{

    private Time cow;
    private CountDownLatch latch;

    public Master(Time cow, CountDownLatch latch)
    {
        this.cow = cow;
        this.latch = latch;
    }

    public void run()
    {
        try
        {
            latch.await();
            while (true)
            {
                TimeUnit.SECONDS.sleep(5);
                cow.plusAdin();

            }
        } catch (InterruptedException ex)
        {
            Logger.getLogger(Master.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

class Time
{

    private int time;
    private int offset;

    public Time(int time, int offset)
    {
        this.time = time;
        this.offset = offset;
    }

    public int getTime()
    {
        return time;
    }

    public int getOffset()
    {
        return offset;
    }

    @Override
    public String toString()
    {
        return "Time{" + "time=" + time + ", offset=" + offset + '}';
    }
    Lock lock = new ReentrantLock();

    public synchronized void incrementTime() throws InterruptedException
    {
        ArrayList l = new ArrayList();
Iterator i = l.iterator();
        
        if (time % 2 == 0)
        {
            wait();

        }
        time++;
        offset++;
        System.out.println("up!" + time);


    }

    public synchronized void plusAdin() throws InterruptedException
    {
        time++;
        offset++;
        System.out.println("fixed!" + time);
        notify();
    }
}
